package com.luych.oauth2Module.controller;

import com.luych.common.constant.UserMessageConstant;
import com.luych.common.page.Page;
import com.luych.common.util.ServletUtils;
import com.luych.oauth2Module.controller.vo.Oauth2ClientVo;
import com.luych.oauth2Module.entitey.*;
import com.luych.oauth2Module.service.Oauth2ClientService;
import com.luych.oauth2Module.service.Oauth2TokenService;
import com.luych.oauth2Module.service.UserService;
import com.sun.javafx.binding.StringFormatter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

@RestController
public class Oauth2Controller {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Oauth2ClientService oauth2ClientService;
    private final Oauth2TokenService oauth2TokenService;
    private final UserService userService;

    @Autowired
    public Oauth2Controller(Oauth2ClientService oauth2ClientService, Oauth2TokenService oauth2TokenService, UserService userService) {
        this.oauth2ClientService = oauth2ClientService;
        this.oauth2TokenService = oauth2TokenService;
        this.userService = userService;
    }

    /**
     * Oauth2Client分页查询
     *
     * @param page     分页参数
     * @param keywords 查询关键字
     * @return 分页结果
     */
    @GetMapping("oauth2/client")
    @PreAuthorize("hasAuthority('getOauth2Client')")
    public ResponseEntity<Page> page(Page page, String keywords) throws InvocationTargetException, IllegalAccessException, IOException {
        page = oauth2ClientService.pageClient(keywords, page);
        List<Oauth2Client> oauth2ClientVos = new ArrayList<>(page.getRecords().size());
        for (int i = 0; i < page.getRecords().size(); i++) {
            Oauth2Client oauth2Client = (Oauth2Client) page.getRecords().get(i);
            Oauth2ClientVo oauth2ClientVo = new Oauth2ClientVo();
            BeanUtils.copyProperties(oauth2ClientVo, oauth2Client);
            oauth2ClientVo.setScopes(oauth2ClientService.getScopeByClientId(oauth2Client.getClientId()));
            oauth2ClientVos.add(oauth2ClientVo);
        }
        page.setRecords(oauth2ClientVos);
        return new ResponseEntity<>(page, HttpStatus.OK);
    }

    /**
     * 新增Oauth2Client
     *
     * @param oauth2Client Oauth2Client实体
     * @return 返回新增后反查的Oauth2Client
     */
    @PutMapping(value = "oauth2/client", consumes = MediaType.APPLICATION_JSON_VALUE)
    @PreAuthorize("hasAuthority('addOauth2Client')")
    public ResponseEntity<Oauth2Client> add(@RequestBody Oauth2Client oauth2Client) {
        if (StringUtils.isEmpty(oauth2Client.getClientId())) {
            oauth2Client.setClientId(UUID.randomUUID().toString().replace("-", ""));
        }
        if (StringUtils.isEmpty(oauth2Client.getClientSecret())) {
            oauth2Client.setClientSecret(UUID.randomUUID().toString().replace("-", ""));
        }
        return new ResponseEntity<>(oauth2ClientService.addClient(oauth2Client), HttpStatus.OK);
    }

    /**
     * 修改Oauth2Client
     *
     * @param clientId     Oauth2Client.clientId
     * @param oauth2Client Oauth2Client实体
     * @return 返回编辑后反查的Oauth2Client
     */
    @PostMapping(value = "oauth2/client/{clientId}", consumes = MediaType.APPLICATION_JSON_VALUE)
    @PreAuthorize("hasAuthority('modifyOauth2Client')")
    public ResponseEntity<Oauth2Client> modify(@PathVariable String clientId, @RequestBody Oauth2Client oauth2Client) {
        oauth2Client.setClientId(clientId);
        return new ResponseEntity<>(oauth2ClientService.modifyClient(oauth2Client), HttpStatus.OK);
    }

    /**
     * 删除Oauth2Client
     *
     * @param clientId Oauth2Client.clientId
     * @return Oauth2Client.clientId
     */
    @DeleteMapping(value = "oauth2/client/{clientId}")
    @PreAuthorize("hasAuthority('removeOauth2Client')")
    public ResponseEntity<String> remove(@PathVariable String clientId) {
        oauth2ClientService.removeClient(clientId);
        return new ResponseEntity<>(clientId, HttpStatus.OK);
    }

    /**
     * 根据Oauth2Client.clientId查询Oauth2Client
     *
     * @param clientId Oauth2Client.clientId
     * @return Oauth2Client
     */
    @GetMapping("oauth2/client/{clientId}")
    public Oauth2Client oauth2ClientByClientId(@PathVariable String clientId) {
        return oauth2ClientService.getByClientId(clientId);
    }

    /**
     * 获取授权码
     *
     * @param clientId     Oauth2Client.clientId
     * @param responseType 授权类型，固定值 code
     * @param redirectUri  客户端回调地址
     * @param scope        申请授权范围
     * @param state        客户端当前状态，可以指定任意值，认证服务器会原封不动地返回这个值。
     * @param request      HttpServletRequest
     * @param response     HttpServletResponse
     */
    @GetMapping("oauth2/authorize")
    public void authorize(@RequestParam String clientId, @RequestParam String responseType, @RequestParam String redirectUri,
                          String scope, String state, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // responseType 不能为空
        if (StringUtils.isEmpty(responseType)) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_NeedParam, "responseType").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        // responseType 的值固定为code
        if (!responseType.equals("code")) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "responseType").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        // 判断clientId是否有效
        Oauth2Client oauth2Client = oauth2ClientService.getByClientId(clientId);
        if (oauth2Client == null) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "clientId").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        // 判断scope是否有效
        List<Oauth2Scope> oauth2Scopes = oauth2ClientService.getScopeByClientId(clientId);
        if (oauth2Scopes == null || oauth2Scopes.size() == 0) {
            logger.error("client[" + clientId + "] scope can't be empty");
            ServletUtils.httpResponseError(response, HttpStatus.INTERNAL_SERVER_ERROR.value(), UserMessageConstant.error_ServerError);
            return;
        }
        List<String> settingScopes = new ArrayList<>(oauth2Scopes.size());
        for (Oauth2Scope oauth2Scope : oauth2Scopes) {
            settingScopes.add(oauth2Scope.getScopeName());
        }
        List<String> paramScopes = Arrays.asList(scope.split(" "));
        if (!settingScopes.containsAll(paramScopes)) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "scope").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        if (StringUtils.isEmpty(oauth2Client.getRedirectUrl())) {
            logger.error("client[" + clientId + "] redirectUrl can't be empty");
            ServletUtils.httpResponseError(response, HttpStatus.INTERNAL_SERVER_ERROR.value(), UserMessageConstant.error_ServerError);
            return;
        }
        // 判断redirectUrl是否有效
        String[] redirectUris = oauth2Client.getRedirectUrl().split(",");
        if (!Arrays.asList(redirectUris).contains(redirectUri)) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "redirectUri").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        // 检查当前是否有用户登录
        SecurityContext securityContext = SecurityContextHolder.getContext();
        UserDetails userDetails = (UserDetails) securityContext.getAuthentication().getPrincipal();
        if (userDetails == null) {
            ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), UserMessageConstant.error_NeedUserLogin);
            return;
        }
        UserInfo currUser = userService.getUserByLoginName(userDetails.getUsername());
        // 查看当前是否有可用的code
        Oauth2Code oauth2Code = oauth2TokenService.getAuthenticationCode(clientId, currUser.getUserId());
        if (oauth2Code != null) {
            oauth2TokenService.saveAuthenticationCode(clientId, currUser.getUserId(), oauth2Code.getCode(), redirectUri, scope);
            ServletUtils.httpResponseOK(response, oauth2Code.getCode());
            return;
        }
        // 创建并保存一个code
        String code = UUID.randomUUID().toString();
        oauth2TokenService.saveAuthenticationCode(clientId, currUser.getUserId(), code, redirectUri, scope);
        ServletUtils.httpResponseOK(response, code);
    }

    @GetMapping("oauth2/token")
    public void token(@RequestParam String clientId, @RequestParam String clientSecret, @RequestParam String grantType,
                      String code, String redirectUri, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 判断client是否有效
        Oauth2Client oauth2Client = oauth2ClientService.getByClientId(clientId);
        if (oauth2Client == null) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "clientId").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.BAD_REQUEST.value(), errorMsg);
            return;
        }
        if (!oauth2Client.getClientSecret().equals(clientSecret)) {
            String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "clientSecret").getValue();
            ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), errorMsg);
            return;
        }
        if (grantType.equals("authorization_code")) {
            // 检查当前是否有用户登录
            SecurityContext securityContext = SecurityContextHolder.getContext();
            UserDetails userDetails = (UserDetails) securityContext.getAuthentication().getPrincipal();
            if (userDetails == null) {
                ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), UserMessageConstant.error_AuthenticationCodeExpire);
                return;
            }
            UserInfo currUser = userService.getUserByLoginName(userDetails.getUsername());
            Oauth2Code oauth2Code = oauth2TokenService.getAuthenticationCode(clientId, currUser.getUserId());
            if (!oauth2Code.getCode().equals(code)) {
                String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "code").getValue();
                ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), errorMsg);
                return;
            }
            if (!oauth2Code.getRedirectUrl().equals(redirectUri)) {
                String errorMsg = StringFormatter.format(UserMessageConstant.error_UnvalidateParam, "redirectUri").getValue();
                ServletUtils.httpResponseError(response, HttpStatus.FORBIDDEN.value(), errorMsg);
                return;
            }
        }
    }

}
